package org.chainsale.bean;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.chainsale.common.Info;

public class Practitioner {

	public static final Map<Integer, List<Integer>> LAYER_MAP = new HashMap<Integer, List<Integer>>();
	public static int SYSTEM_TOTAL_NUMBER = 0;
	
	public static int SYSTEM_TOTAL_OUTGOING = 0;
	public static int SYSTEM_TOTAL_INCOMING = 0;
	
	
	private int id;
	private String name;
	private int share;
	private int outgoing;
	private int incoming;
	private Rank rank;
	private Rank oldRank;
	private boolean isUpToDirector = false;
	private boolean isUpToManager = true;
	private Practitioner superior;
	private List<Practitioner> underlings = new ArrayList<Practitioner>(3);
	private int layer = 0;
	private List<Integer> allUIds = new ArrayList<Integer>();

	public void addUnderling(Practitioner p) {
		if (underlings.size() < 3 && !underlings.contains(p)) {
			
			
			// pair register
			underlings.add(p);
			p.setSuperior(this);
			
			p.setLayer(p.getSuperior().getLayer() + 1);
			putToLayerMap(p.getLayer(), p.getId());
			SYSTEM_TOTAL_NUMBER++;
			
			// money assignment on this chain
			addIncoming4Chain(false, this, p.getOutgoing());
			

			// share changes on this chain
			addShare4Chain(false, this, p.getShare());
			
			// rank changes on this chain
			changeRank4Chain(false, this);
			
			
			// for level promote
			addBonus4Chain(false, p, p.getShare());
			
			// for add underling id on this chain
			addFollowingUId4Chain(false, this, p.getId());
			
		} else {
			System.out.println("P["+id+"]'s underlings' number already >= 3 or already contians this P[" + p.getId() + "].");
		}
	}
	
	public static void putToLayerMap(int layer, int pId) {
		List<Integer> pIds = LAYER_MAP.get(layer);
		
		if (pIds == null) {
			pIds = new ArrayList<Integer>();
			LAYER_MAP.put(layer, pIds);
		}
		
		pIds.add(pId);
	}
	
	private boolean addShare4Chain(boolean isTop, Practitioner p, int addedShare) {
		p.addShare(addedShare);
		while (!isTop && isChainNotEnded(p)) {
			p = p.getSuperior();
			isTop = addShare4Chain(isTop, p, addedShare);
		}
		return isTop ? true : isChaindEnded(p);
	}
	
	private boolean addFollowingUId4Chain(boolean isTop, Practitioner p, Integer uId) {
		p.getAllUIds().add(uId);
		while (!isTop && isChainNotEnded(p)) {
			p = p.getSuperior();
			isTop = addFollowingUId4Chain(isTop, p, uId);
		}
		return isTop ? true : isChaindEnded(p);
	}
	
	private boolean changeRank4Chain(boolean isTop, Practitioner p) {
		// change p's rank by rule
		Rank newRank = Rank.regenerateRank(p);
		Rank currRank = p.getRank();
		if (newRank.getLevel() > currRank.getLevel()) {
			int currRankLevel = currRank.getLevel();
			int newRankLevel = newRank.getLevel();
			if (currRankLevel == 1 || currRankLevel == 2) {
				if (newRankLevel == 3 || newRankLevel == 4 || newRankLevel == 5) {
					p.isUpToDirector = true;
				}
			} else if (currRankLevel == 3 || currRankLevel == 4 || currRankLevel == 5) {
				if (newRankLevel == 6 || newRankLevel == 7 || newRankLevel == 8) {
					p.isUpToManager = true;
				}
			}
			
			p.setOldRank(currRank);
			p.setRank(newRank);
		}
		
		while (isChainNotEnded(p)) {
			p = p.getSuperior();
			isTop = changeRank4Chain(isTop, p);
		}
		return isTop ? true : isChaindEnded(p);
	}
	
	private boolean addIncoming4Chain(boolean isTop, Practitioner p, int totalMoney) {
		
		p.addIncoming((int) (totalMoney * p.getRank().getPercentage()));
		
		while (!isTop && isChainNotEnded(p)) {
			p = p.getSuperior();
			isTop = addIncoming4Chain(isTop, p, totalMoney);
		}
		
		return isTop ? true : isChaindEnded(p);
	}
	
	private boolean addBonus4Chain(boolean isTop, Practitioner p, int addShares) {
		
		if (p.getRank().getLevel() >= 9) {
			return true;
		}
		
		if (p.isUpToDirector) {
			int added = (int) (addShares * Info.getInstance().getMoneyPerShare() * 0.04);
			p.addIncoming(added);
			
			Practitioner manager = p.getSuperior();
			
			while (manager.getRank().getLevel() < 6) {
				manager = manager.getSuperior();
			}
			
			manager.addOutgoing(added);
			
			p.isUpToDirector = false;
		} else if (p.isUpToManager) {
			
			
			
			p.isUpToManager = false;
		}
		
		while (!isTop && isChainNotEnded(p)) {
			p = p.getSuperior();
			isTop = addBonus4Chain(isTop, p, addShares);
		}
		
		return isTop ? true : isChaindEnded(p);
	}
	
	private boolean isChainNotEnded(Practitioner p) {
		return p.getSuperior() != null && p.getSuperior().getRank().getLevel() < Rank.OUTCHIEF.getLevel();
	}
	
	private boolean isChaindEnded(Practitioner p) {
		return !isChainNotEnded(p);
	}
	
	public void addOutgoing(int outgoing) {
		SYSTEM_TOTAL_OUTGOING += outgoing;
		this.outgoing += outgoing;
	}
	
	public void addIncoming(int incoming) {
		SYSTEM_TOTAL_INCOMING += incoming;
		this.incoming += incoming;
	}
	
	public void addShare(int added) {
		this.share += added;
	}
	
	public static List<Integer> getPracIdsByLayer(int layer) {
		return LAYER_MAP.get(layer);
	}
	
	
	
	// getters, setters
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getShare() {
		return share;
	}

	public void setShare(int share) {
		this.share = share;
	}

	public int getOutgoing() {
		return outgoing;
	}

//	public void setOutgoing(int outgoing) {
//		this.outgoing = outgoing;
//	}

	public int getIncoming() {
		return incoming;
	}

//	public void setIncoming(int incoming) {
//		this.incoming = incoming;
//	}

	public List<Practitioner> getUnderlings() {
		return underlings;
	}

	public Practitioner getSuperior() {
		return superior;
	}

	public void setSuperior(Practitioner superior) {
		this.superior = superior;
	}

	public Rank getRank() {
		return rank;
	}
	
	public void setRank(Rank rank) {
		this.rank = rank;
	}

	public Rank getOldRank() {
		return oldRank;
	}

	public void setOldRank(Rank oldRank) {
		this.oldRank = oldRank;
	}
	
	public int getLayer() {
		return layer;
	}
	
	public void setLayer(int layer) {
		this.layer = layer;
	}
	
	public List<Integer> getAllUIds() {
		return allUIds;
	}

	public String getDescription() {
		return new StringBuilder(getName()).append("[").append(getShare()).append("]").toString();
	}
	
}
